#include "ImageBuffer.h"


ImageBuffer::ImageBuffer(int _w, int _h, int _numLayers){
	//printf("Created an ImageBuffer\n");
	imgW = _w;
	imgH = _h;
	layerCount = _numLayers;
	//initialize the vectors
	theImage = vector<vector<vec4>>(_numLayers);
	for (int l = 0; l < _numLayers; l++){
		theImage[l] = vector<vec4>(imgW * imgH);
	}
	initMinMax();
	theActiveLayer = 0;
}

ImageBuffer::ImageBuffer(ImageBuffer* _other){
	//copy the values from the other one
	imgW = _other->width();
	imgH = _other->height();
	layerCount = _other->layers();
	int pixelCount = imgW * imgH;
	theImage.resize(imgH);
	for (int l = 0; l < layerCount; l++){
		_other->activeLayer(l);
		vector<vec4> next = _other->pixels();
		theImage[l] = vector<vec4>(next);
	}
	
}

ImageBuffer::~ImageBuffer(){
}


//set a pixel value
void ImageBuffer::pixel(int _x, int _y, vec4 _col){
	if (_x < 0 || _x >= imgW || _y < 0 || _y >= imgH){
		return;
	}
	int i = (imgW * _y) + _x;
	theImage[theActiveLayer][i] = _col;
}
void ImageBuffer::pixel(int _x, int _y, vec3 _col){
	pixel(_x, _y, vec4(_col, 0));
}

//get a pixel value
vec4 ImageBuffer::pixel(int _x, int _y){
	if (_x < 0 || _x >= imgW || _y < 0 || _y >= imgH){
		return vec4();
	}
	//check for out of bounds
	int i = (imgW * _y) + _x;
	return theImage[theActiveLayer][i];
}

void ImageBuffer::clear(){
	//set all pixels to black
	for (int i = 0; i < layerCount; i++){
		for (int y = 0; y < imgH; y++){
			for (int x = 0; x < imgW; x++){
				theImage[i][imgW * y + x] = vec4();
			}
		}
	}
	
}

//perform a convolution
void ImageBuffer::convolve(mat3 _kernel, int _sourceLayer, int _destinationLayer){
	initMinMax();
	//convolve the _sourceLayer and store it in _destinationLayer
	//perform the convolution on each color channel separately and store the result into its own channel
	for (int y = 0; y < imgH; y++){
		for (int x = 0; x < imgW; x++){
			int up = (y > 0) ? y - 1 : y;
			int down = (y < imgH - 1) ? y + 1 : y;
			int left = (x > 0) ? x - 1 : x;
			int right = (x < imgW - 1) ? x + 1 : x;


		}
	}
}

//use a Sobel operator to detect edges
//also, store the min and max values
void ImageBuffer::edgeDetect(int _sourceLayer, int _destinationLayer){
	//make sure the layers exist
	if (layerCount < _sourceLayer + 1 || layerCount < _destinationLayer + 1){
		//NOTE: this does not ensure that the source layer actually has any data
		layers(max(_sourceLayer + 1, _destinationLayer + 1));
	}
	initMinMax();
	//this matrix is in column-major format
	mat3 h_kernel = mat3(vec3(-1, -2, -1),	//column 1
						vec3(0, 0, 0),		//column 2
						vec3(1, 2, 1));		//column 3
	
	mat3 v_kernel = mat3(vec3(1, 0, -1),	//column 1
						vec3(2, 0, -2),		//column 2
						vec3(1, 0, -1));	//column 3
	for (int y = 0; y < imgH; y++){
		for (int x = 0; x < imgW; x++){
			vec4 h_val = convolve(h_kernel, _sourceLayer, x, y);
			vec4 v_val = convolve(v_kernel, _sourceLayer, x, y);
			h_val /= 8.0f;
			v_val /= 8.0f;
			
			float r_val = sqrtf(pow(h_val.r, 2) + pow(v_val.r, 2));
			float g_val = sqrtf(pow(h_val.g, 2) + pow(v_val.g, 2));
			float b_val = sqrtf(pow(h_val.b, 2) + pow(v_val.b, 2));

			//if any of these values are the new highest or lowest, store them as such
			updateMinMax(_destinationLayer, r_val);
			updateMinMax(_destinationLayer, g_val);
			updateMinMax(_destinationLayer, b_val);

			theImage[_destinationLayer][pixIndex(x, y)] = vec4(r_val, g_val, b_val, 1);
		}
	}
}

void ImageBuffer::updateMinMax(int _layer, float _a){
	if (_a < theMinMaxVals[_layer][0])
		theMinMaxVals[_layer][0] = _a;
	else if (_a > theMinMaxVals[_layer][1])
		theMinMaxVals[_layer][1] = _a;
}

vec4 ImageBuffer::convolve(mat3 _kernel, int _sourceLayer, int _x, int _y){
	int up = (_y > 0) ? _y - 1 : _y;
	int down = (_y < imgH - 1) ? _y + 1 : _y;
	int left = (_x > 0) ? _x - 1 : _x;
	int right = (_x < imgW - 1) ? _x + 1 : _x;

	float r_val = 0;
	r_val += _kernel[0][0] * theImage[_sourceLayer][pixIndex(left, up)].r;
	r_val += _kernel[1][0] * theImage[_sourceLayer][pixIndex(_x, up)].r;
	r_val += _kernel[2][0] * theImage[_sourceLayer][pixIndex(right, up)].r;
	r_val += _kernel[0][1] * theImage[_sourceLayer][pixIndex(left, _y)].r;
	r_val += _kernel[1][1] * theImage[_sourceLayer][pixIndex(_x, _y)].r;
	r_val += _kernel[2][1] * theImage[_sourceLayer][pixIndex(right, _y)].r;
	r_val += _kernel[0][2] * theImage[_sourceLayer][pixIndex(left, down)].r;
	r_val += _kernel[1][2] * theImage[_sourceLayer][pixIndex(_x, down)].r;
	r_val += _kernel[2][2] * theImage[_sourceLayer][pixIndex(right, down)].r;

	float g_val = 0;
	g_val += _kernel[0][0] * theImage[_sourceLayer][pixIndex(left, up)].g;
	g_val += _kernel[1][0] * theImage[_sourceLayer][pixIndex(_x, up)].g;
	g_val += _kernel[2][0] * theImage[_sourceLayer][pixIndex(right, up)].g;
	g_val += _kernel[0][1] * theImage[_sourceLayer][pixIndex(left, _y)].g;
	g_val += _kernel[1][1] * theImage[_sourceLayer][pixIndex(_x, _y)].g;
	g_val += _kernel[2][1] * theImage[_sourceLayer][pixIndex(right, _y)].g;
	g_val += _kernel[0][2] * theImage[_sourceLayer][pixIndex(left, down)].g;
	g_val += _kernel[1][2] * theImage[_sourceLayer][pixIndex(_x, down)].g;
	g_val += _kernel[2][2] * theImage[_sourceLayer][pixIndex(right, down)].g;

	float b_val = 0;
	b_val += _kernel[0][0] * theImage[_sourceLayer][pixIndex(left, up)].b;
	b_val += _kernel[1][0] * theImage[_sourceLayer][pixIndex(_x, up)].b;
	b_val += _kernel[2][0] * theImage[_sourceLayer][pixIndex(right, up)].b;
	b_val += _kernel[0][1] * theImage[_sourceLayer][pixIndex(left, _y)].b;
	b_val += _kernel[1][1] * theImage[_sourceLayer][pixIndex(_x, _y)].b;
	b_val += _kernel[2][1] * theImage[_sourceLayer][pixIndex(right, _y)].b;
	b_val += _kernel[0][2] * theImage[_sourceLayer][pixIndex(left, down)].b;
	b_val += _kernel[1][2] * theImage[_sourceLayer][pixIndex(_x, down)].b;
	b_val += _kernel[2][2] * theImage[_sourceLayer][pixIndex(right, down)].b;

	return vec4(r_val, g_val, b_val, 1);
}

int ImageBuffer::pixIndex(int _x, int _y){
	return (_y * imgW) + _x;
}

void ImageBuffer::toGrey(int _mode, int _sourceLayer, int _destinationLayer){
	initMinMax();
	int pixelCount = imgW * imgH;
	if (_mode == 0){		//average the RGB values
		for (int i = 0; i < pixelCount; i++){
			vec4 orig = theImage[_sourceLayer][i];
			float avgVal = (orig.r + orig.g + orig.b) / 3.0f;
			vec4 avgCol = vec4(avgVal, avgVal, avgVal, 1);
			theImage[_destinationLayer][i] = avgCol;
		}
	}
	else if (_mode == 1){	//pick the highest RGB value
		for (int i = 0; i < pixelCount; i++){
			vec4 orig = theImage[_sourceLayer][i];
			float hiVal = max(max(orig.r, orig.g), max(orig.r, orig.b));
			updateMinMax(_destinationLayer, hiVal);
			vec4 avgCol = vec4(hiVal, hiVal, hiVal, 1);
			theImage[_destinationLayer][i] = avgCol;
		}
	}
}

//return a pointer to an array of pixel values
float* ImageBuffer::toFloatArray(){
	return NULL;
}

void ImageBuffer::resize(int _width, int _height){	//NOTE: this function destroys the contents of the image
	imgW = _width;
	imgH = _height;
	for (int l = 0; l < layerCount; l++){
		theImage[l] = vector<vec4>(imgW * imgH);
	}
}

/*vector<vec4> ImageBuffer::rowAt(int _r){
	return theImage[_r];
}*/

void ImageBuffer::layers(int _l){
	if (layerCount != _l){	//only resize if necessary
		theImage.resize(_l);
		//resize the new layers
		for (int i = layerCount; i < _l; i++){
			theImage[i] = vector<vec4>(imgW * imgH);
		}
		theMinMaxVals.resize(_l);
		layerCount = _l;
	}
}

vector<vec4> ImageBuffer::pixels(){
	return theImage[theActiveLayer];
}

void ImageBuffer::activeLayer(int _l){
	if (_l < 0){
		theActiveLayer = 0;
	}
	else if (_l >= layerCount){
		theActiveLayer = layerCount - 1;
	}
	else{
		theActiveLayer = _l;
	}
	
}

//get the minimum and maximum values of a particular layer
vec2 ImageBuffer::minMaxVals(int _l){
	return theMinMaxVals[_l];
}

void ImageBuffer::initMinMax(){
	theMinMaxVals = vector<vec2>(layerCount);
	for (int l = 0; l < layerCount; l++){
		theMinMaxVals[l] = vec2(1e30f, 0);
	}
}
